package com.koudai.finance.utils;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.util.*;
import java.util.concurrent.*;

/**
 * @Description: 异步操作工具类
 * @author: ls
 * @Date: 2020/5/22 8:57
 */
@Slf4j
@Component
public class AsyncUtils {

    /**
     * 配置线程池中的核心线程数
     */
    @Value("${thread.corePoolSize:8}")
    private Integer corePoolSize;

    /**
     * 配置最大线程数
     */
    @Value("${thread.maxPoolSize:16}")
    private Integer maxPoolSize;

    /**
     * 超时时间
     */
    @Value("${thread.timeOut:5000}")
    private long timeOut;

    /**
     * 配置队列大小
     */
    @Value("${thread.queueCapacity:100}")
    private Integer queueCapacity;

    private ListeningExecutorService service = null;

    /**
     *  @Description: 初始化线程池对象
     *  @author: ls
     *  @Date: 2020/5/22 9:19
     */
    @PostConstruct
    public void initialize() {
        ExecutorService executorService = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
                timeOut, TimeUnit.SECONDS, new LinkedBlockingQueue<>(queueCapacity),
                new ThreadFactoryBuilder().setNameFormat("rx-thread-%d").build(), new ThreadPoolExecutor.CallerRunsPolicy());
        service = MoreExecutors.listeningDecorator(executorService);
    }

    /**
     * @Description: 返回单个对象
     * @author: ls
     * @Date: 2020/5/22 8:43
     */
    public <T> ListenableFuture<T> getFuture(Callable<T> callable) {
        return service.submit(callable);
    }

    /**
     * @Description: 无返回值
     * @author: ls
     * @Date: 2020/5/22 8:43
     */
    public ListenableFuture getFuture(Runnable task) {
        return service.submit(task);
    }

    /**
     * @Description: 获取列表返回值
     * @author: ls
     * @Date: 2020/5/22 8:43
     */
    public <T> List<T> getValueList(List<ListenableFuture<T>> futureList) {
        List<T> result = Lists.newArrayListWithCapacity(futureList.size());
        if (CollectionUtils.isEmpty(futureList)) {
            return result;
        }
        try {
            ListenableFuture<List<T>> allFuture = Futures.allAsList(futureList);
            result = Futures.getChecked(allFuture, Exception.class);
            return result;
        } catch (Exception ex) {
            log.error("", ex);
            return Lists.newArrayList();
        }
    }

    /**
     * @Description: 获取列表返回值
     * @author: ls
     * @Date: 2020/5/22 8:43
     */
    public <T> List<Future<T>> getFutures(Collection<? extends Callable<T>> tasks) {
        List<Future<T>> futures = new ArrayList<Future<T>>();
        try {
            futures = service.invokeAll(tasks, timeOut, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            log.error("failed to invoke all callable task", e);
        }
        return futures;
    }

    /**
     * @Description: 直接获取单个对象结果
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <T> T getGenericResult(Future<T> future) {
        if (null == future) {
            return null;
        }
        T t = null;
        try {
            t = future.get();
        } catch (Exception e) {
            log.error("failed to get result from future", e);
        }
        return t;
    }

    /**
     * @Description: 直接获取单个对象结果
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <T> T getResult(Future future) {
        if (null == future) {
            return null;
        }
        T t = null;
        try {
            t = (T) future.get();
        } catch (Exception e) {
            log.error("failed to get result from future", e);
        }
        return t;
    }

    /**
     * @Description: 批量取回Map结果
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <K, V> Map<K, V> getMapResults(List<Future<Map<K, V>>> futures) {
        Map<K, V> result = new HashMap<K, V>();
        if (CollectionUtils.isEmpty(futures)) {
            return result;
        }
        Map<K, V> map;
        for (Future<Map<K, V>> future : futures) {
            map = getGenericResult(future);
            if (MapUtils.isNotEmpty(map)) {
                result.putAll(map);
            }
        }
        return result;
    }

    /**
     * @Description: 批量取回Map结果
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <K, V> Map<K, V> getMapResultAll(List<Future> futures) {
        Map<K, V> result = new HashMap<K, V>();
        if (CollectionUtils.isEmpty(futures)) {
            return result;
        }
        Map<K, V> map;
        for (Future future : futures) {
            map = getResult(future);
            if (MapUtils.isNotEmpty(map)) {
                result.putAll(map);
            }
        }
        return result;
    }

    /**
     * @Description: 批量取回结果
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <T> List<T> getListResults(List<Future<List<T>>> futures) {
        List<T> result = new ArrayList<T>();
        if (CollectionUtils.isEmpty(futures)) {
            return result;
        }
        List<T> list;
        for (Future<List<T>> future : futures) {
            list = getGenericResult(future);
            if (null != list) {
                result.addAll(list);
            }
        }
        return result;
    }

    /**
     * @Description: 批量接口取回
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <T> List<T> getResults(List<Future> futures) {
        if (CollectionUtils.isEmpty(futures)) {
            return Lists.newArrayList();
        }
        List<T> result = Lists.newArrayListWithCapacity(futures.size());
        for (Future future : futures) {
            T t = getResult(future);
            if (t != null) {
                result.add(t);
            }
        }
        return result;
    }

    /**
     * @Description: 批量接口取回
     * @author: ls
     * @Date: 2020/5/22 8:49
     */
    public <T> List<T> getResultAll(List<Future<T>> futures) {
        if (CollectionUtils.isEmpty(futures)) {
            return Lists.newArrayList();
        }

        List<T> result = Lists.newArrayListWithCapacity(futures.size());
        for (Future<T> future : futures) {
            T t = getGenericResult(future);
            if (t != null) {
                result.add(t);
            }
        }
        return result;
    }
}
